package cn.com.yang.pressureTest;

import java.util.LinkedList;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Author 杨亿金
 * @Date 2021/6/18 9:53
 * @CopyRight https://my.oschina.net/tantexian/blog/1839432
 * @Version
 */
public abstract class BaseBenchMarker {

    public void startup(int threadCount) throws Exception {
        System.out.printf("start ... threadCount ==  %d ---->>>>(请覆盖toBeTestMethod()方法为自定义测试逻辑)\n", threadCount);

        final ExecutorService sendThreadPool = Executors.newFixedThreadPool(threadCount);

        final StatsBenchmark statsBenchmark = new StatsBenchmark();

        final Timer timer = new Timer("BenchmarkTimerThread", true);

        final LinkedList<Long[]> snapshotList = new LinkedList<Long[]>();

        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                snapshotList.addLast(statsBenchmark.createSnapshot());
                if (snapshotList.size() > 10) {
                    snapshotList.removeFirst();
                }
            }
        }, 1000, 1000);

        timer.scheduleAtFixedRate(new TimerTask() {
            private void printStats() {
                if (snapshotList.size() >= 10) {
                    Long[] begin = snapshotList.getFirst();
                    Long[] end = snapshotList.getLast();

                    // index含义：0-代表当前时间，1-发送成功数量 2-发送失败数量 3-接收成功数量 4-接收失败数量 5-发送消息成功总耗时
                    final long sendTps =
                            (long) (((end[3] - begin[3]) / (double) (end[0] - begin[0])) * 1000L);
                    final double averageRT = ((end[5] - begin[5]) / (double) (end[3] - begin[3]));

                    System.out.printf(
                            "Send TPS: %d Max RT: %d Average RT: %7.3f Send Failed: %d Response Failed: %d\n"//
                            , sendTps//
                            , statsBenchmark.getSendMessageMaxRT().get()//
                            , averageRT//
                            , end[2]//
                            , end[4]//
                    );
                }
            }


            @Override
            public void run() {
                try {
                    this.printStats();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }, 10000, 10000);


        for (int i = 0; i < threadCount; i++) {
            sendThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        try {
                            final long beginTimestamp = System.currentTimeMillis();
                            toBeTestMethod();
                            statsBenchmark.getSendRequestSuccessCount().incrementAndGet();
                            statsBenchmark.getReceiveResponseSuccessCount().incrementAndGet();
                            final long currentRT = System.currentTimeMillis() - beginTimestamp;
                            statsBenchmark.getSendMessageSuccessTimeTotal().addAndGet(currentRT);
                            long prevMaxRT = statsBenchmark.getSendMessageMaxRT().get();
                            while (currentRT > prevMaxRT) {
                                boolean updated =
                                        statsBenchmark.getSendMessageMaxRT().compareAndSet(prevMaxRT,
                                                currentRT);
                                if (updated)
                                    break;

                                prevMaxRT = statsBenchmark.getSendMessageMaxRT().get();
                            }
                        } catch (Exception e) {
                            statsBenchmark.getSendRequestFailedCount().incrementAndGet();
                            e.printStackTrace();

                            try {
                                Thread.sleep(3000);
                            } catch (InterruptedException e1) {
                            }
                        }
                    }
                }
            });
        }

    }

    /**
     * 业务使用方，覆盖掉此方法即可以执行自定义压测方法
     *
     * @param
     * @author tantexian
     * @since 2018/7/3
     */
    public void toBeTestMethod() throws Exception {
        // 随机模拟sleep1~1000毫秒时间
        try {
            final int millis = new Random().nextInt(1000);
            Thread.sleep(millis);
            // 模拟如果时间为30毫秒倍数，则失败，抛出异常
            if (millis % 500 == 0) {
                throw new Exception("模拟失败，抛出异常");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
